package org.zoomdev.zoom.timer.impl;

import org.quartz.*;
import org.quartz.core.jmx.JobDataMapSupport;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zoomdev.zoom.common.Destroyable;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.utils.Classes;
import org.zoomdev.zoom.common.utils.MapUtils;
import org.zoomdev.zoom.timer.TimerJob;
import org.zoomdev.zoom.timer.TimerService;
import org.zoomdev.zoom.timer.modules.CoreTimerModule;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public final class QuartzTimerService implements TimerService, Destroyable {
    public static final String JOB_DATA = "job_data";
    private static final Logger log = LoggerFactory.getLogger(CoreTimerModule.NAME);
    private final Scheduler scheduler;
    private final ConcurrentHashMap<String, JobCreateInfo> map = new ConcurrentHashMap<>();


    public QuartzTimerService() {
        try {
            scheduler = StdSchedulerFactory.getDefaultScheduler();
        } catch (SchedulerException e) {
            throw new ZoomException(e);
        }

        if (scheduler == null) {
            throw new ZoomException("scheduler is null");
        }
    }

    static class JobCreateInfo {
        final Trigger trigger;
        final JobDetail detail;
        final String name;
        final String cron;
        volatile boolean running;

        public JobCreateInfo(Trigger trigger,
                             JobDetail detail,
                             String cron,
                             String name) {
            this.trigger = trigger;
            this.detail = detail;
            this.cron = cron;
            this.name = name;
            this.running = true;
        }

        public JobKey getJobKey() {
            return trigger.getJobKey();
        }

        public TriggerKey getTriggerKey() {
            return trigger.getKey();
        }


    }

    public static class SimpleTimerJob implements Job {

        @SuppressWarnings({"rawtypes", "unchecked"})
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            JobDataMap map = context.getJobDetail().getJobDataMap();
            Object data = map.get(JOB_DATA);
            try {
                TimerJob runnable = (TimerJob) data;
                runnable.execute();
            } catch (Throwable e) {
                Throwable throwable = Classes.getCause(e);
                log.error("执行定时任务出错", throwable);
                if (throwable instanceof JobExecutionException) {
                    throw (JobExecutionException) throwable;
                }
                throw new JobExecutionException(throwable);
            }
        }
    }

    @Override
    public void destroy() {
        log.info("Shutting down timer service");
        stopAll();
        map.clear();
        try {
            scheduler.shutdown();
        } catch (SchedulerException e) {
            log.error("关闭定时任务发生异常", e);
        }
    }

    @Override
    public void start(String jobName, TimerJob job, String cron) {
        map.computeIfAbsent(jobName, k -> {
            JobBuilder jobBuilder = JobBuilder.newJob(SimpleTimerJob.class).withIdentity("jobName", k);
            Map<String, Object> jobData = MapUtils.asMap(JOB_DATA, job);
            jobBuilder.setJobData(JobDataMapSupport.newJobDataMap(jobData));
            JobDetail jobDetail = jobBuilder.build();

            CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule(cron)
                    .withMisfireHandlingInstructionDoNothing();

            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger", k)
                    .withSchedule(cronSchedule).build();
            try {
                scheduler.scheduleJob(jobDetail, trigger);
                scheduler.start();
                return new JobCreateInfo(trigger, jobDetail, cron, k);
            } catch (Exception e) {
                throw new ZoomException("创建定时任务失败", e);
            }
        });
    }

    @Override
    public void stop(String jobName) {
        map.computeIfPresent(jobName, (k, v) -> {
            Trigger trigger = v.trigger;
            TriggerKey key = trigger.getKey();
            try {
                scheduler.unscheduleJob(v.getTriggerKey());
                log.info("Close task trigger:{}", key.getName());
            } catch (SchedulerException e) {
                log.error("unscheduleJob异常", e);
            }
            return null;
        });
    }

    @Override
    public Set<String> jobNames() {
        return map.keySet();
    }

    @Override
    public void resume(String jobName) {
        JobCreateInfo info = map.get(jobName);
        if (info == null) {
            log.info("找不到名称为[{}]的定时器", jobName);
            return;
        }
        try {
            scheduler.resumeJob(info.getJobKey());
        } catch (SchedulerException e) {
            log.error("恢复timer {} 发生异常", jobName, e);
        }
    }

    @Override
    public void pause(String jobName) {
        JobCreateInfo info = map.get(jobName);
        if (info == null) {
            log.info("找不到名称为[{}]的定时器", jobName);
            return;
        }
        try {
            scheduler.pauseJob(info.getJobKey());
        } catch (SchedulerException e) {
            log.error("停止timer {} 发生异常", jobName, e);
        }
    }

    @Override
    public void stopAll() {
        Set<String> set = new HashSet<>(map.keySet());
        for (String jobName : set) {
            stop(jobName);
        }
    }


}
